use nom::branch::alt;
use nom::bytes::complete::{tag, take_while, take_while1, take_while_m_n};
use nom::combinator::map;
use nom::multi::count;
use nom::sequence::delimited;
use nom::IResult;

use std::fmt;

#[derive(Debug)]
pub enum Reply {
    //状态回复
    Status(String),
    //错误回复
    Error(String),
    //整数回复
    Int(i64),
    //批量回复
    Bulk(Option<String>),
    //多条批量回复
    MultiBulk(Option<Vec<Reply>>),
}

impl Reply {
    pub fn display(&self) -> String {
        match self {
            Reply::Status(s) => format!("{}\n", s),
            Reply::Error(s) => format!("{}\n", s),
            Reply::Int(s) => format!("{}\n", s),
            Reply::Bulk(opt) => {
                if let Some(s) = opt {
                    format!("{}\n", s)
                } else {
                    format!("NULL\n")
                }
            }
            Reply::MultiBulk(opt) => {
                if let Some(reply_vec) = opt {
                    if reply_vec.len() == 0 {
                        format!("Empty\n")
                    } else {
                        let mut res = String::new();
                        for reply in reply_vec.iter() {
                            res = res + &reply.display()
                        }
                        res
                    }
                } else {
                    format!("NULL\n")
                }
            }
        }
    }
}

//状态回复
pub fn parse_status(i: &str) -> IResult<&str, Reply> {
    // let (input, _) = tag("+")(i)?;
    // let (input, res) = take_while(|c| c != '\r' && c != '\n')(input)?;
    // let (input, _) = tag("\r\n")(input)?;
    // Ok((input, Reply::Status(res.to_string())))
    let (i, res) = delimited(
        tag("+"),
        take_while(|c| c != '\r' && c != '\n'),
        tag("\r\n"),
    )(i)?;
    Ok((i, Reply::Status(res.to_string())))
}

//错误回复
pub fn parse_error(i: &str) -> IResult<&str, Reply> {
    // let (input, _) = tag("-")(i)?;
    // let (input, res) = take_while(|c| c != '\r' && c != '\n')(input)?;
    // let (input, _) = tag("\r\n")(input)?;
    // Ok((input, Reply::Status(res.to_string())))
    let (i, res) = delimited(
        tag("-"),
        take_while1(|c| c != '\r' && c != '\n'),
        tag("\r\n"),
    )(i)?;
    Ok((i, Reply::Error(res.to_string())))
}

//整数回复
pub fn parse_int(i: &str) -> IResult<&str, Reply> {
    let (i, res) = delimited(
        tag(":"),
        map(
            take_while1(|c: char| c.is_digit(10) || c == '-'),
            |i: &str| i.parse::<i64>().unwrap(),
        ),
        tag("\r\n"),
    )(i)?;
    Ok((i, Reply::Int(res)))
}

pub fn parse_bulk(i: &str) -> IResult<&str, Reply> {
    let (i, _) = tag("$")(i)?;
    let (i, len) = take_while1(|c: char| c.is_digit(10) || c == '-')(i)?;
    if len == "-1" {
        let (i, _) = tag("\r\n")(i)?;
        Ok((i, Reply::Bulk(None)))
    } else {
        let len = len.parse::<usize>().unwrap();
        let (i, res) = delimited(tag("\r\n"), take_while_m_n(len, len, |_| true), tag("\r\n"))(i)?;
        Ok((i, Reply::Bulk(Some(res.to_string()))))
    }
}

pub fn parse_muti_bulk(i: &str) -> IResult<&str, Reply> {
    let (i, counts) = delimited(
        tag("*"),
        take_while1(|c: char| c.is_digit(10) || c == '-'),
        tag("\r\n"),
    )(i)?;
    if counts == "-1" {
        Ok((i, Reply::MultiBulk(None)))
    } else if counts == "0" {
        Ok((i, Reply::MultiBulk(Some(Vec::new()))))
    } else {
        let counts = counts.parse::<usize>().unwrap();
        let (i, res): (&str, Vec<Reply>) = count(
            alt((parse_status, parse_error, parse_int, parse_bulk)),
            counts,
        )(i)?;
        Ok((i, Reply::MultiBulk(Some(res))))
    }
}

pub fn parse(i: &str) -> IResult<&str, Reply> {
    alt((
        parse_muti_bulk,
        parse_bulk,
        parse_int,
        parse_error,
        parse_status,
    ))(i)
}
